package untils

import (
	"base_srv/basic/config"
	"bufio"
	"bytes"
	"context"
	"errors"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
	"io"
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

// 获取OSS客户端
func GetOSSClient() *oss.Client {
	con := config.ConAll.Oss
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewStaticCredentialsProvider(con.AccessKeyID, con.AccessKeySecret)).
		WithRegion(con.YuMing)
	return oss.NewClient(cfg)
}

// oss简单上传
func OssUpload(objectName string, url string) error {
	con := config.ConAll.Oss
	client := GetOSSClient()
	// 填写要上传的本地文件路径和文件名称，例如 /Users/localpath/exampleobject.txt
	localFile := url

	// 创建上传对象的请求
	putRequest := &oss.PutObjectRequest{
		Bucket:       oss.Ptr(con.BucketName),  // 存储空间名称
		Key:          oss.Ptr(objectName),      // 对象名称
		StorageClass: oss.StorageClassStandard, // 指定对象的存储类型为标准存储
		Acl:          oss.ObjectACLPublicRead,  // 指定对象的访问权限为公共读
	}

	// 执行上传对象的请求
	result, err := client.PutObjectFromFile(context.TODO(), putRequest, localFile)
	if err != nil {
		return errors.New("oss上传失败:" + err.Error())
	}
	// 打印上传对象的结果
	log.Printf("put object from file result:%#v\n", result)
	return nil
}

// OssMultipartUpload 分片上传文件到OSS
func OssMultipartUpload(objectName string, url string) error {
	con := config.ConAll.Oss
	client := GetOSSClient()

	initRequest := &oss.InitiateMultipartUploadRequest{
		Bucket: oss.Ptr(con.BucketName),
		Key:    oss.Ptr(objectName),
	}
	initResult, err := client.InitiateMultipartUpload(context.TODO(), initRequest)
	if err != nil {
		return errors.New("oss分片上传初始化失败:" + err.Error())
	}

	// 打印初始化分片上传的结果
	//log.Printf("initiate multipart upload result:%#v\n", *initResult.UploadId)
	uploadId := *initResult.UploadId

	// 初始化等待组和互斥锁
	var wg sync.WaitGroup
	var parts []oss.UploadPart
	count := 3
	var mu sync.Mutex

	// 读取本地文件内容到内存，将yourLocalFile替换为实际的本地文件名和路径
	file, err := os.Open(url)
	if err != nil {
		return errors.New("oss分片上传打开文件失败:" + err.Error())
	}
	defer file.Close()

	bufReader := bufio.NewReader(file)
	content, err := io.ReadAll(bufReader)
	if err != nil {
		return errors.New("oss分片上传读取文件失败:" + err.Error())
	}
	// log.Printf("file size: %d\n", len(content))

	// 计算每个分片的大小
	chunkSize := len(content) / count
	if chunkSize == 0 {
		chunkSize = 1
	}

	// 启动多个goroutine进行分片上传
	for i := 0; i < count; i++ {
		start := i * chunkSize
		end := start + chunkSize
		if i == count-1 {
			end = len(content)
		}

		wg.Add(1)
		go func(partNumber int, start, end int) {
			defer wg.Done()

			// 创建分片上传请求
			partRequest := &oss.UploadPartRequest{
				Bucket:     oss.Ptr(con.BucketName),             // 目标存储空间名称
				Key:        oss.Ptr(objectName),                 // 目标对象名称
				PartNumber: int32(partNumber),                   // 分片编号
				UploadId:   oss.Ptr(uploadId),                   // 上传ID
				Body:       bytes.NewReader(content[start:end]), // 分片内容
			}

			// 发送分片上传请求
			partResult, err := client.UploadPart(context.TODO(), partRequest)
			if err != nil {
				log.Fatalf("failed to upload part %d: %v", partNumber, err)
			}

			// 记录分片上传结果
			part := oss.UploadPart{
				PartNumber: partRequest.PartNumber,
				ETag:       partResult.ETag,
			}

			// 使用互斥锁保护共享数据
			mu.Lock()
			parts = append(parts, part)
			mu.Unlock()
		}(i+1, start, end)
	}

	// 等待所有goroutine完成
	wg.Wait()

	// 完成分片上传请求
	request := &oss.CompleteMultipartUploadRequest{
		Bucket:   oss.Ptr(con.BucketName),
		Key:      oss.Ptr(objectName),
		UploadId: oss.Ptr(uploadId),
		CompleteMultipartUpload: &oss.CompleteMultipartUpload{
			Parts: parts,
		},
	}
	result, err := client.CompleteMultipartUpload(context.TODO(), request)
	if err != nil {
		return errors.New("oss分片上传完成分片上传失败:" + err.Error())
	}

	// 打印完成分片上传的结果
	log.Printf("complete multipart upload result:%#v\n", result)
	return nil
}

// 获取OSS文件URL
func GetOssUrl(bucketName, objectName string) string {

	// 创建OSS客户端
	client := GetOSSClient()

	// 生成GetObject的预签名URL
	result, err := client.Presign(context.TODO(), &oss.GetObjectRequest{
		Bucket: oss.Ptr(bucketName),
		Key:    oss.Ptr(objectName),
	},
		oss.PresignExpires(10*time.Minute),
	)
	if err != nil {
		log.Fatalf("failed to get object presign %v", err)
	}

	log.Printf("request method:%v\n", result.Method)
	log.Printf("request expiration:%v\n", result.Expiration)
	log.Printf("request url:%v\n", result.URL)
	return result.URL
}

// DeleteOssFile 删除OSS文件
func DeleteOssFile(objectName string) error {
	con := config.ConAll.Oss
	client := GetOSSClient()

	// 创建删除对象的请求
	deleteRequest := &oss.DeleteObjectRequest{
		Bucket: oss.Ptr(con.BucketName), // 存储空间名称
		Key:    oss.Ptr(objectName),     // 对象名称
	}

	// 执行删除对象的请求
	_, err := client.DeleteObject(context.TODO(), deleteRequest)
	if err != nil {
		log.Printf("failed to delete object %v: %v", objectName, err)
		return err
	}

	// 打印删除对象的结果
	log.Printf("delete object %v success", objectName)
	return nil
}

// DeleteApplicationFiles 删除应用相关文件（安装包和图片）
func DeleteApplicationFiles(objectName string) error {
	// 假设ObjectName格式为：app_[appId]_[filename].[ext] 或类似格式
	// 我们需要从中提取appId，然后删除该应用的所有相关文件

	// 解析ObjectName，提取应用ID
	parts := strings.Split(objectName, ".")
	if len(parts) < 2 {
		return errors.New("ObjectName format not supported")
	}

	appId := parts[0]

	// 获取OSS客户端
	con := config.ConAll.Oss
	client := GetOSSClient()

	// 列出所有匹配前缀的文件
	listRequest := &oss.ListObjectsRequest{
		Bucket: oss.Ptr(con.BucketName),
		Prefix: oss.Ptr(appId),
	}

	listResult, err := client.ListObjects(context.TODO(), listRequest)
	if err != nil {
		return errors.New("failed to list objects with prefix " + appId + " " + err.Error())
	}

	// 删除所有匹配的文件，但排除正在上传的文件
	for _, object := range listResult.Contents {
		if *object.Key != objectName {
			_ = DeleteOssFile(*object.Key)
		}
	}
	return nil
}
